XML Storage Options in Delphi
By Mike - father@bigattichouse.com
Many programs we may write may need to store complex data in files on disk, be able to retreive those files, and be able to send those files in emails to others to use in their projects, on various operating systems.
Accompanying source code is available at: http://www.bigattichouse.com/projectfiles.zip
Picking a storage method:
The file mechanisms built into pascal seem well enough for simple project files of repeated types (File of Record), or straight text, but do not satusfy some of the very complex structures we may encounter (for our example lets consider a file of polygons having any number of points.)
XML immediately jumps into mind, and your first inclination may be to attempt building a solution using MSXML, or some other heavy weight. This, of couse, removes Kylix from the picture and seriously limits your chances when it comes to cross-compiling.
So lets assume we want to use XML to store our data, we now need a fast and reliable way to transfer our objects from Delphi to our project file format.
A sample "Project" using classes:
For our sample project, we have a three dimensional point class, a line class that connect points, a polygon that contains a collection of lines, and a project containing a collection of polygon. Complex enough? See the downloadable code for the fully implemented classes (proper constructors, etc).
The Objects look something like this:
TPolyPoint = class (TPersistent)So now we can create a project, add Polygons, add lines to the polygons, maybe even have a little code to render the polygons on a TImage.. great! Creating a single file format would require something like ESRI's SHP formats.. yuck! So how exactly are we supposed to get the code into and out of that XML file? We could build some convoluted code to use MSXML to read and write the Objects, which wouldn't be very reusable or we could use something provided by the great minds of Scott's valley: RTTI!
contains X,Y,Z for a point
TPolyLine = class(TCollectionItem)
has an origin and destination PolyPoint
TPolyGon = class(TCollectionItem)
contains a collection of lines
TProject = class(TPersistent)
contains a collection of PolyGons
RTTI and XML:
You may have noticed that everything I descended from was TPersistent. There is a reason for this. All those fields you see hanging out in the PUBLISHED section are available for a generic pulling of info and building XML.
Then you think, it would take me 3 months to write code to generically pull XML out of objects and put it back. In reality it takes about 10 minutes with an existing FREE tool. You can download QuickRTTI from Big Attic House and use it for free, it is freeware as long as you leave the credits in the source code alone. And you have to only make a few lines of code to be able to read store and basically have a nice transferrable project:
1) Change the Project class= TPersistent to TXMLAwareWhat do the enablers do?
2) Pick an XML enabler (lowX,middlex,MSXML)
3) SavetoXML or Load from XML!
QuickRTTI is a generic RTTI reader and writer, you can use it to programmatically access the properties of objects that you know nothing about at run time. An XMLEnabler is a class designed to run through those properties and generate XML or read XML.
There are currently 3 parsers implemented:Other Parsers can be implemented upon request.
LowX - a VERY poor parser that gets confused on complex constructs but is VERY fast, simple objects with out a lot of TCollection and subobjects work VERY well with LowX. LowX will get confused if you have XML in string properties MiddleX - a slightly newer parser, which does not use schemas, but is VERY fast and written in Delphi.. middleX uses a node/leaf system to parse content and has proven very stable. MSXML - thats right, it uses the MSXML parser, works well, but is VERY heavy and much slower that the other two.
So Show us how to use it!
{Assumes TProject is now TProject=class(TXMLAware),QuickRTTI will recurse through the properties and pull out all the collection items, maintaining their types, and continue through, so that your ENTIRE project will be safely and reliably stored, and pulled back for later use.
Memo1 is a TMemo on the main form}
var Q:TMiddleXQuickRTTI; P1,P2:TProject;
begin
Q:=TMiddleXQuickRTTI.create;{Saving to XML and freeing}
P:=TProject.create(Q);
Memo1.text:= P.savetoXML;
P.free;{Creating a new one and Loading project from an XML string}
P2:=TProject.create(Q);
P2.loadfromXML (Memo1.text)
P2.free;Q.free;
end;
What other uses can we put this to?
Big Attic House is currently working on a TupleSpace implementation for Delphi like JSpaces and J-Space ("D"-space?), as well as a generic tool to publish TPersistent Objects via PureSOAP and XML-RPC with minimal coding. Take a look at the source code and see what you can find... together we'll build better tools.